#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <windows.h>

#define WIDTH 50
#define HEIGHT 20
#define SNAKE_MAX_LENGTH 100


typedef enum { UP, DOWN, LEFT, RIGHT } Direction;


typedef struct {
    int x;
    int y;
} Point;


typedef struct {
    Point body[SNAKE_MAX_LENGTH];
    int length;
    Direction direction;
    int score;
} Snake;


typedef struct {
    Point position;
    int eaten;
} Food;


Snake snake;
Food food;
int gameOver;
int highScore = 0;
int sleepTime = 150; 
int scorePerFood = 1; 


void initGame();
void createMap();
void createfood();
void snakemove();
void keyboardControl();
void cantcrosswall();
int biteself(); 
void scoreandtips();
void gameOverScreen();
void gotoxy(int x, int y);
void setColor(int color);
void clearScreen();
void File_out();
void speedup();
void speeddown();
void welcometogame();
void explain();
void showStartScreen();

// Main function
int main() {
    srand(time(NULL));
    int choice;
    
    while (1) {
        welcometogame();
        scanf("%d", &choice);
        fflush(stdin); 

        switch (choice) {
            case 1:
                showStartScreen();
                break;
            case 2:
                explain();
                break;
            default:
                printf("Invalid choice, please re-enter!\n");
                Sleep(1000);
        }
    }
}


void welcometogame() {
    clearScreen();
    setColor(14);
    printf("\n\n\n");
    printf("                 Snake Game\n");
    printf("========================================\n");
    printf("              1. Enter start page\n");
    printf("              2. Game instructions\n");
    printf("========================================\n");
    printf("Please choose: ");
}


void showStartScreen() {
    clearScreen();
    setColor(14); 
    printf("\n\n\n");
    printf("                 Start Game\n");
    printf("========================================\n");
    printf("              1. Start new game\n");
    printf("              2. View high score\n");
    printf("              3. Exit game\n"); 
    printf("========================================\n");
    printf("Please choose: ");
    
    int choice;
    scanf("%d", &choice);
    fflush(stdin); 
    
    switch (choice) {
        case 1:
            initGame();
            while (!gameOver) {
                clearScreen();
                createMap();
                keyboardControl();
                snakemove();
                cantcrosswall();
                scoreandtips();
                Sleep(sleepTime);
            }
            gameOverScreen();
            break;
        case 2:
            File_out();
            break;
        case 3: 
            clearScreen();
            printf("\nThanks for playing!\n");
            exit(0);
            
        default:
            printf("Invalid choice, please re-enter!\n");
            Sleep(1000);
    }
}


void explain() {
    clearScreen();
    setColor(14);
    printf("\n\n\n");
    printf("                 Game Instructions\n");
    printf("========================================\n");
    printf("  Control methods:\n");
    printf("    Arrow keys (up down left right) to control snake movement\n");
    printf("    F1: Speed up    F2: Slow down\n");
    printf("    ESC: Exit game\n");
    printf("\n  Game rules:\n");
    printf("    - Eat food to increase score and length\n");
    printf("    - Game over if hitting walls or itself\n");
    printf("    - Faster speed, higher score per food\n");
    printf("========================================\n");
    printf("              1. Return to initial interface\n");
    printf("========================================\n");
    printf("Please choose: ");
    
    int choice;
    while (scanf("%d", &choice) != 1 || choice != 1) {
        while (getchar() != '\n');
        printf("Invalid choice, please enter 1 to return: ");
    }
    while (getchar() != '\n'); 
}


void initGame() {
    
    FILE *fp = fopen("save.txt", "r");
    if (fp != NULL) {
        fscanf(fp, "%d", &highScore);
        fclose(fp);
    }

  
    snake.length = 3;
    snake.body[0].x = WIDTH / 2;
    snake.body[0].y = HEIGHT / 2;
    for (int i = 1; i < snake.length; i++) {
        snake.body[i].x = snake.body[i - 1].x - 1;
        snake.body[i].y = snake.body[i - 1].y;
    }
    snake.direction = RIGHT;
    snake.score = 0;

   
    createfood();

    gameOver = 0;
    sleepTime = 150;   
    scorePerFood = 2;   
}


void createMap() {
    clearScreen();

   
    setColor(15); 
    for (int i = 0; i < WIDTH + 2; i++) {
        printf("# ");
    }
    printf("\n");

    
    for (int y = 0; y < HEIGHT; y++) {
        setColor(15); 
        printf("# ");
        for (int x = 0; x < WIDTH; x++) {
            int isSnake = 0;
           
            if (x == snake.body[0].x && y == snake.body[0].y) {
                setColor(12); 
                printf(" O");
                isSnake = 1;
            }
           
            else {
                for (int i = 1; i < snake.length; i++) {
                    if (x == snake.body[i].x && y == snake.body[i].y) {
                        setColor(10); 
                        printf(" +");
                        isSnake = 1;
                        break;
                    }
                }
            }

          
            if (x == food.position.x && y == food.position.y) {
                setColor(13); 
                printf(" *");
                isSnake = 1;
            }

            
            if (!isSnake) {
                printf("  ");
            }
        }
        setColor(15);
        printf(" #\n"); 
    }

    
    setColor(15);
    for (int i = 0; i < WIDTH + 2; i++) {
        printf("# ");
    }
    printf("\n");

   
    setColor(14); 
    printf("Score: %d  High Score: %d  Current Score/Food: %d\n", snake.score, highScore, scorePerFood);
    printf("Speed: %dms  (F1 to speed up, F2 to slow down)\n", sleepTime);
}


void createfood() {
    do {
        food.position.x = rand() % WIDTH;
        food.position.y = rand() % HEIGHT;

        
        int onSnake = 0;
        for (int i = 0; i < snake.length; i++) {
            if (food.position.x == snake.body[i].x && food.position.y == snake.body[i].y) {
                onSnake = 1;
                break;
            }
        }

        if (!onSnake) {
            food.eaten = 0;
            break;
        }
    } while (1);
}


void snakemove() {
    
    for (int i = snake.length - 1; i > 0; i--) {
        snake.body[i] = snake.body[i - 1];
    }

    
    switch (snake.direction) {
        case UP:
            snake.body[0].y--;
            break;
        case DOWN:
            snake.body[0].y++;
            break;
        case LEFT:
            snake.body[0].x--;
            break;
        case RIGHT:
            snake.body[0].x++;
            break;
    }

  
    if (snake.body[0].x == food.position.x && snake.body[0].y == food.position.y) {
        snake.length++;
        snake.score += scorePerFood;  
        createfood();

        
        speedup();
    }
}


void keyboardControl() {
    if (_kbhit()) {
        int key = _getch();

       
        if (key == 0 || key == 224) {
            key = _getch();

            switch (key) {
                case 59: 
                    speedup();
                    break;
                case 60: 
                    speeddown();
                    break;
                case 72: 
                    if (snake.direction != DOWN)
                        snake.direction = UP;
                    break;
                case 80:
                    if (snake.direction != UP)
                        snake.direction = DOWN;
                    break;
                case 75: 
                    if (snake.direction != RIGHT)
                        snake.direction = LEFT;
                    break;
                case 77:
                    if (snake.direction != LEFT)
                        snake.direction = RIGHT;
                    break;
            }
        } else { 
            switch (key) {
                case 27: 
                    gameOver = 1;
                    break;
            }
        }
    }
}


void cantcrosswall() {
  
    if (snake.body[0].x < 0 || snake.body[0].x >= WIDTH ||
        snake.body[0].y < 0 || snake.body[0].y >= HEIGHT) {
        gameOver = 1;
    }
    
    
    if (biteself()) {
        gameOver = 1;
    }
}


int biteself() {
    
    for (int i = 1; i < snake.length; i++) {
        if (snake.body[0].x == snake.body[i].x && snake.body[0].y == snake.body[i].y) {
            return 1; 
        }
    }
    return 0; 
}


void scoreandtips() {
    if (snake.score > highScore) {
        highScore = snake.score;

        
        FILE *fp = fopen("save.txt", "w");
        if (fp != NULL) {
            fprintf(fp, "%d", highScore);
            fclose(fp);
        }
    }
}


void gameOverScreen() {
    int choice;
    clearScreen();
    setColor(12); 
    printf("\n\n\n");
    printf("                  GAME OVER\n");
    printf("========================================\n");
    if (snake.score >= highScore) {
        printf("Congratulations on breaking the high score record, the new record is: %d\n", highScore);
    } else {
        printf("Unfortunately~ %d points away from the high score\n", highScore - snake.score);
    }
    printf("========================================\n");
    printf("1: Restart     2: Return to main menu     3: Exit game\n");
    printf("========================================\n");
    printf("Please choose: ");
    
    while (1) {
        if (scanf("%d", &choice) != 1) {
           
            while (getchar() != '\n');
            printf("Invalid input, please enter a number: ");
            continue;
        }
        while (getchar() != '\n');
        
        switch (choice) {
            case 1: 
                showStartScreen();
                return;
                
            case 2: 
                return;
                
            case 3: 
                clearScreen();
                printf("\nThanks for playing!\n");
                exit(0);
                
            default:
                printf("Invalid choice, please re-enter: ");
        }
    }
}


void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}


void setColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}


void clearScreen() {
    system("cls");
}


void File_out() {
    clearScreen();
    setColor(14); 
    printf("\n\n\n");
    printf("                 High Score Records\n");
    printf("========================================\n");


    FILE *fp = fopen("save.txt", "r");
    if (fp != NULL) {
        int score;
        if (fscanf(fp, "%d", &score) == 1) {
            printf("Current high score: %d\n", score);
        } else {
            printf("No records yet\n");
        }
        fclose(fp);
    } else {
        printf("No records yet\n");
    }

    printf("========================================\n");
    printf("Press any key to return...");
    getch();
}


void speedup() {
    
    sleepTime -= 30;
    if (sleepTime < 10) sleepTime = 10; 

   
    scorePerFood = (320 - sleepTime) / 30 + 2; 
}


void speeddown() {
    
    sleepTime += 30;
    if (sleepTime > 350) sleepTime = 350; 

  
    scorePerFood = (320 - sleepTime) / 30 + 2;
}
